package org.ns.summer.mvc.mappinghandler;

import org.ns.summer.aop.AopUtils;
import org.ns.summer.beans.BeanFactoryPostProcessor;
import org.ns.summer.beans.ConfigurableBeanFactory;
import org.ns.summer.beans.InitializingBeanPostProcessor;
import org.ns.summer.beans.anns.Component;
import org.ns.summer.beans.error.BeansException;
import org.ns.summer.core.AnnotationUtils;
import org.ns.summer.core.MethodUtils;
import org.ns.summer.mvc.anns.Controller;
import org.ns.summer.mvc.anns.RequestMapping;
import org.ns.summer.mvc.dynamicexec.ExecParamTemplate;
import org.ns.summer.mvc.fuzzysearch.FuzzySearch;
import org.ns.summer.mvc.utils.MvcUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Component
public class UrlHandlerMapping implements HandlerMapping, InitializingBeanPostProcessor, BeanFactoryPostProcessor {
    // url -> HandlerExecutionChain
    private FuzzySearch<HandlerExecutionChain> search = new FuzzySearch<HandlerExecutionChain>();
    private List<HandlerInterceptor> handlerInterceptors = new ArrayList<>();
    private List<ExecParamTemplate> paramTemplates = new ArrayList<>();
    private ConfigurableBeanFactory beanFactory;
    @Override
    public void postProcessBeanFactory(ConfigurableBeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        loadHandlerInterceptor(beanFactory);
        loadParamTemplate(beanFactory);
    }

    private void loadHandlerInterceptor(ConfigurableBeanFactory beanFactory) {
        String[] handlerInterceptorBeanNames = beanFactory.getBeanNamesForType(HandlerInterceptor.class);
        for (String beanName : handlerInterceptorBeanNames) {
            HandlerInterceptor handlerInterceptor = beanFactory.getBean(beanName, HandlerInterceptor.class);
            handlerInterceptors.add(handlerInterceptor);
        }
    }
    private void loadParamTemplate(ConfigurableBeanFactory beanFactory) {
        paramTemplates.add(new ParamAnnotationTemplate()); // 默认的参数注入
        String[] handlerInterceptorBeanNames = beanFactory.getBeanNamesForType(ExecParamTemplate.class);
        for (String beanName : handlerInterceptorBeanNames) {
            ExecParamTemplate paramTemplate = beanFactory.getBean(beanName, ExecParamTemplate.class);
            paramTemplates.add(paramTemplate);
        }
    }

    @Override
    public void preHandler(ConfigurableBeanFactory beanFactory, Object bean, String beanName) throws BeansException {
        Class<?> targetType = AopUtils.getTargetType(bean.getClass());
        registerExecutorMethod(beanFactory, bean, beanName, targetType);
    }

    private void registerExecutorMethod(ConfigurableBeanFactory beanFactory, Object bean, String beanName, Class<?> targetType) {
        if (!AnnotationUtils.existAnnotation(targetType, Controller.class)) {
            return;
        }
        if (!AnnotationUtils.existAnnotation(targetType, RequestMapping.class)) {
            return;
        }
        RequestMapping rootRequestMapping = (RequestMapping)AnnotationUtils.getAnnotation(targetType, RequestMapping.class);

        List<Method> methods = MethodUtils.findMethodByAnnotation(targetType, RequestMapping.class);
        for (Method method : methods) {
            registerExecutorMethod(beanFactory, rootRequestMapping, method, beanName);
        }
    }

    private void registerExecutorMethod (ConfigurableBeanFactory beanFactory, RequestMapping rootRequestMapping, Method method, String beanName) {
        RequestMapping requestMapping = (RequestMapping)AnnotationUtils.getAnnotation(method, RequestMapping.class);
        RequestMethod[] requestMethods = requestMapping.method();
        if (requestMethods.length == 0) {
            requestMethods = rootRequestMapping.method();
        }
        if (requestMethods.length == 0) {
            requestMethods = new RequestMethod[]{RequestMethod.GET,RequestMethod.POST};
        }


        HandlerMethod handlerMethod = new HandlerMethod(method, beanName, () -> beanFactory.getBean(beanName));
        HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain(handlerMethod, paramTemplates, handlerInterceptors);
        for (RequestMethod requestMethod : requestMethods) {
            String methodMod = requestMethod.name();
            String url = MvcUtils.margeUrl(methodMod, rootRequestMapping.path(), requestMapping.path());
            this.search.add(url, handlerExecutionChain);
        }
    }

    @Override
    public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        String methodMod = request.getMethod().toUpperCase();
        String url = MvcUtils.margeUrl(methodMod, request.getRequestURI());
        return this.search.match(url);
    }

    public List<HandlerInterceptor> getHandlerInterceptors() {

        return handlerInterceptors;
    }
}
